package com.tonyodev.fetch2.fetch;

import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.utils.Pair;

import com.tonyodev.fetch2.CompletedDownload;
import com.tonyodev.fetch2.Download;
import com.tonyodev.fetch2.EnqueueAction;
import com.tonyodev.fetch2.Error;
import com.tonyodev.fetch2.FetchErrorUtils;
import com.tonyodev.fetch2.FetchGroup;
import com.tonyodev.fetch2.FetchListener;
import com.tonyodev.fetch2.FetchNotificationManager;
import com.tonyodev.fetch2.NetworkType;
import com.tonyodev.fetch2.PrioritySort;
import com.tonyodev.fetch2.Request;
import com.tonyodev.fetch2.Status;
import com.tonyodev.fetch2.database.DownloadInfo;
import com.tonyodev.fetch2.database.FetchDatabaseManagerWrapper;
import com.tonyodev.fetch2.downloader.DownloadManager;
import com.tonyodev.fetch2.exception.FetchException;
import com.tonyodev.fetch2.helper.PriorityListProcessor;
import com.tonyodev.fetch2.provider.GroupInfoProvider;
import com.tonyodev.fetch2.util.Defaults;
import com.tonyodev.fetch2.util.FetchUtils;
import com.tonyodev.fetch2.util.TypeConverterExtensions;
import com.tonyodev.fetch2core.DownloadBlock;
import com.tonyodev.fetch2core.DownloadBlockInfo;
import com.tonyodev.fetch2core.Downloader;
import com.tonyodev.fetch2core.Extras;
import com.tonyodev.fetch2core.FetchCoreUtils;
import com.tonyodev.fetch2core.FetchErrorStrings;
import com.tonyodev.fetch2core.FetchObserver;
import com.tonyodev.fetch2core.FileResource;
import com.tonyodev.fetch2core.FileServerDownloader;
import com.tonyodev.fetch2core.FileSliceInfo;
import com.tonyodev.fetch2core.InterruptMonitor;
import com.tonyodev.fetch2core.Logger;
import com.tonyodev.fetch2core.Reason;
import com.tonyodev.fetch2core.StorageResolver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Condition;

import timber.log.Timber;

import static com.tonyodev.fetch2core.FetchErrorStrings.REQUEST_WITH_FILE_PATH_ALREADY_EXIST;
import static com.tonyodev.fetch2core.FetchErrorStrings.RESPONSE_NOT_SUCCESSFUL;

/**
 * @since 2021-05-20
 */
public class FetchHandlerImpl implements FetchHandler {
    private int listenerId = UUID.randomUUID().hashCode();
    private LinkedHashSet<FetchListener> listenerSet = new LinkedHashSet<>();

    private String namespace;
    private FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper;
    private DownloadManager downloadManager;
    private PriorityListProcessor<Download> priorityListProcessor;
    private Logger logger;
    private Boolean autoStart;
    private Downloader httpDownloader;
    private FileServerDownloader fileServerDownloader;
    private ListenerCoordinator listenerCoordinator;
    private EventHandler uiHandler;
    private StorageResolver storageResolver;
    private FetchNotificationManager fetchNotificationManager;
    private GroupInfoProvider groupInfoProvider;
    private PrioritySort prioritySort;
    private Boolean createFileOnEnqueue;
    private Boolean isTerminating = false;

    public FetchHandlerImpl(String namespace,
                            FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper,
                            DownloadManager downloadManager,
                            PriorityListProcessor<Download> priorityListProcessor,
                            Logger logger,
                            boolean autoStart,
                            Downloader<?, ?> httpDownloader,
                            FileServerDownloader fileServerDownloader,
                            ListenerCoordinator listenerCoordinator,
                            EventHandler uiHandler,
                            StorageResolver storageResolver,
                            FetchNotificationManager fetchNotificationManager,
                            GroupInfoProvider groupInfoProvider,
                            PrioritySort prioritySort,
                            boolean createFileOnEnqueue) {
        this.namespace = namespace;
        this.fetchDatabaseManagerWrapper = fetchDatabaseManagerWrapper;
        this.downloadManager = downloadManager;
        this.priorityListProcessor = priorityListProcessor;
        this.logger = logger;
        this.autoStart = autoStart;
        this.httpDownloader = httpDownloader;
        this.fileServerDownloader = fileServerDownloader;
        this.listenerCoordinator = listenerCoordinator;
        this.uiHandler = uiHandler;
        this.storageResolver = storageResolver;
        this.fetchNotificationManager = fetchNotificationManager;
        this.groupInfoProvider = groupInfoProvider;
        this.prioritySort = prioritySort;
        this.createFileOnEnqueue = createFileOnEnqueue;

    }

    @Override
    public void init() {
        if (fetchNotificationManager != null) {
               listenerCoordinator.addNotificationManager(fetchNotificationManager);
        }
        fetchDatabaseManagerWrapper.sanitizeOnFirstEntry();
        if (autoStart) {
            priorityListProcessor.start();
        }
    }

    @Override
    public Pair<Download, Error> enqueue(Request request) {
        return enqueueRequests(Collections.singletonList(request)).get(0);
    }

    @Override
    public List<Pair<Download, Error>> enqueue(List<Request> request) {
        return enqueueRequests(request);
    }

    @Override
    public Download enqueueCompletedDownload(CompletedDownload completedDownload) {
        return (Download) enqueueCompletedDownloads((List<CompletedDownload>) Collections.singletonList(completedDownload).get(0));
    }

    @Override
    public List<Pair<DownloadInfo, Boolean>> enqueueBatch(List<Request> requests) throws IOException, DataAbilityRemoteException {
        List<DownloadInfo> downloadInfos = new ArrayList<>();
        for (Request request : requests) {
            DownloadInfo downloadInfo = TypeConverterExtensions.toDownloadInfo(request, fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());
            downloadInfo.setNamespace(namespace);
            Boolean existing = prepareDownloadInfoForEnqueue(downloadInfo);
            if (request.getDownloadOnEnqueue()) {
                downloadInfo.setStatus(Status.QUEUED);
            } else {
                downloadInfo.setStatus(Status.ADDED);
            }
            if (downloadInfo.getStatus() != Status.COMPLETED && !existing) {
                downloadInfos.add(downloadInfo);
            }
        }
        List<Pair<DownloadInfo, Boolean>> results = fetchDatabaseManagerWrapper.insert(downloadInfos);
        startPriorityQueueIfNotStarted();
        return results;

    }

    @Override
    public List enqueueCompletedDownloads(List<CompletedDownload> completedDownloads) {
        List<Download> list = new ArrayList<>();
        for (CompletedDownload completedDownload : completedDownloads) {
            DownloadInfo downloadInfo = TypeConverterExtensions.toDownloadInfo(completedDownload, fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());
            downloadInfo.setNamespace(namespace);
            downloadInfo.setStatus(Status.COMPLETED);
            prepareCompletedDownloadInfoForEnqueue(downloadInfo);
            Pair<DownloadInfo, Boolean> downloadPair = fetchDatabaseManagerWrapper.insert(downloadInfo);
            list.add(downloadPair.f);
        }
        return list;
    }

    private void prepareCompletedDownloadInfoForEnqueue(DownloadInfo downloadInfo) {
        DownloadInfo existingDownload = fetchDatabaseManagerWrapper.getByFile(downloadInfo.getFile());
        if (existingDownload != null) {
            deleteDownloads(Collections.singletonList(downloadInfo));
        }
    }

    @Override
    public List pause(List<Integer> ids) {

        return pauseDownloads(ToEmpty(ids));
        //return pauseDownloads(fetchDatabaseManagerWrapper.get(ids).filterNotNull());
    }

    private List<Download> pauseDownloads(List<DownloadInfo> downloads) {
        cancelDownloadsIfDownloading(downloads);
        List pausedDownloads = new ArrayList<DownloadInfo>();
        for (DownloadInfo download : downloads) {
            if (FetchUtils.canPauseDownload(download)) {
                download.setStatus(Status.PAUSED);
                pausedDownloads.add(download);
            }
        }
        fetchDatabaseManagerWrapper.update(pausedDownloads);
        return pausedDownloads;

    }

    private void cancelDownloadsIfDownloading(List<DownloadInfo> downloads) {
        for (DownloadInfo download : downloads) {
            if (downloadManager.contains(download.getId())) {
                downloadManager.cancel(download.getId());
            }
        }
    }


    @Override
    public List<Download> pausedGroup(int id) {
        return pauseDownloads(fetchDatabaseManagerWrapper.getByGroup(id));
    }

    @Override
    public List<Download> pauseAll() {
        return pauseDownloads(fetchDatabaseManagerWrapper.get());
    }

    @Override
    public void freeze() {
        priorityListProcessor.pause();
        downloadManager.cancelAll();
    }

    @Override
    public void unfreeze() {
        priorityListProcessor.resume();
    }

    @Override
    public List resume(List<Integer> ids) {
        return resumeDownloads(ids);
    }

    private List resumeDownloads(List<Integer> downloadIds) {
        List<DownloadInfo> firstdown = fetchDatabaseManagerWrapper.get(downloadIds);
        ArrayList<DownloadInfo> downloads = new ArrayList<>();
        if (firstdown != null) {
            for (int i = 0; i < firstdown.size(); i++) {
                DownloadInfo downloadInfo = firstdown.get(i);
                if (downloadInfo != null) {
                    downloads.add(downloadInfo);
                }
            }
        }
        ArrayList<DownloadInfo> resumedDownloads = new ArrayList<DownloadInfo>();
        for (DownloadInfo downloadInfos : downloads) {
            if (!downloadManager.contains(downloadInfos.getId()) && FetchUtils.canResumeDownload(downloadInfos)) {
                downloadInfos.setStatus(Status.QUEUED);
                resumedDownloads.add(downloadInfos);
            }
        }
        fetchDatabaseManagerWrapper.update(resumedDownloads);
        startPriorityQueueIfNotStarted();
        return resumedDownloads;
    }


    @Override
    public List resumeGroup(int id) {
        List<Integer> list = new ArrayList<>();
        List<DownloadInfo> loadlist = fetchDatabaseManagerWrapper.getByGroup(id);
        for (DownloadInfo download : loadlist) {
            list.add(download.getId());
        }
        

        return resumeDownloads(list);
    }

    @Override
    public List resumeAll() {
        List<Integer> list = new ArrayList<>();
        List<DownloadInfo> loadlist = fetchDatabaseManagerWrapper.get();
        for (DownloadInfo downloadInfo : loadlist) {
            list.add(downloadInfo.getId());
        }
       
        return resumeDownloads(list);
    }

    @Override
    public List remove(List<Integer> ids) {
        //去空
        List<DownloadInfo> firstdown = fetchDatabaseManagerWrapper.get(ids);
        ArrayList<DownloadInfo> downloads = new ArrayList<>();
        if (firstdown != null) {
            for (int i = 0; i < firstdown.size(); i++) {
                DownloadInfo download = firstdown.get(i);
                if (download != null) {
                    downloads.add(download);
                }
            }
        }

        return removeDownloads(downloads);
    }

    @Override
    public List removeGroup(int id) {
        return removeDownloads(fetchDatabaseManagerWrapper.getByGroup(id));
    }

    @Override
    public List removeAll() {
        return removeDownloads(fetchDatabaseManagerWrapper.get());
    }

    @Override
    public List removeAllWithStatus(Status status) {
        return removeDownloads(fetchDatabaseManagerWrapper.getByStatus(status));
    }

    @Override
    public List removeAllInGroupWithStatus(int groupId, List<Status> statuses) {
        return removeDownloads(fetchDatabaseManagerWrapper.getDownloadsInGroupWithStatus(groupId, statuses));
    }

    private List removeDownloads(List<DownloadInfo> downloads) {
        cancelDownloadsIfDownloading(downloads);
        fetchDatabaseManagerWrapper.delete(downloads);
        for (DownloadInfo download : downloads) {
            download.setStatus(Status.REMOVED);
            if ( fetchDatabaseManagerWrapper.getDelegate() !=null){

                fetchDatabaseManagerWrapper.getDelegate().deleteTempFilesForDownload(download);
            }
        }
        return downloads;
    }

    @Override
    public List delete(List<Integer> ids) {

        List<DownloadInfo> list = fetchDatabaseManagerWrapper.get(ids);
        ArrayList<DownloadInfo> deletedownloads = new ArrayList<>();
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                DownloadInfo download = list.get(i);
                if (download != null) {
                    deletedownloads.add(download);
                }
            }
        }

        return deleteDownloads(deletedownloads);
    }

    @Override
    public List deleteGroup(int id) {
        return deleteDownloads(fetchDatabaseManagerWrapper.getByGroup(id));
    }

    @Override
    public List deleteAll() {
        return deleteDownloads(fetchDatabaseManagerWrapper.get());
    }

    @Override
    public List deleteAllWithStatus(Status status) {
        return deleteDownloads(fetchDatabaseManagerWrapper.getByStatus(status));
    }

    @Override
    public List deleteAllInGroupWithStatus(int groupId, List<Status> statuses) {
        return deleteDownloads(fetchDatabaseManagerWrapper.getDownloadsInGroupWithStatus(groupId, statuses));
    }

    private List deleteDownloads(List<DownloadInfo> downloads) {
        cancelDownloadsIfDownloading(downloads);
        fetchDatabaseManagerWrapper.delete(downloads);
        Timber.d("deleteDownloads.size:" + downloads.size());
        for (DownloadInfo download : downloads) {
            download.setStatus(Status.DELETED);
            try {
                storageResolver.deleteFile(download.getFile());
            } catch (DataAbilityRemoteException e) {
                e.printStackTrace();
            }
            if(fetchDatabaseManagerWrapper.getDelegate()!=null)
              fetchDatabaseManagerWrapper.getDelegate().deleteTempFilesForDownload(download);
        }
        return downloads;
    }


    @Override
    public List cancel(List<Integer> ids) {
        List<DownloadInfo> list = fetchDatabaseManagerWrapper.get(ids);
        ArrayList<DownloadInfo> cancelloads = new ArrayList<>();
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                DownloadInfo download = list.get(i);
                if (download != null) {
                    cancelloads.add(download);
                }
            }
        }
        return cancelDownloads(cancelloads);
    }

    @Override
    public List cancelGroup(int id) {
        return cancelDownloads(fetchDatabaseManagerWrapper.getByGroup(id));
    }

    @Override
    public List cancelAll() {
        return cancelDownloads(fetchDatabaseManagerWrapper.get());
    }

    private List cancelDownloads(List<DownloadInfo> downloads) {
        cancelDownloadsIfDownloading(downloads);
        ArrayList<DownloadInfo> cancelledDownloads = new ArrayList<>();
        for (DownloadInfo download : downloads) {
            if (canCancelDownload(download)) {
                download.setStatus(Status.CANCELLED);
                download.setError(Defaults.getDefaultNoError());
                cancelledDownloads.add(download);
            }
        }
        fetchDatabaseManagerWrapper.update(cancelledDownloads);
        return cancelledDownloads;
    }

    protected Boolean canCancelDownload(Download download) {
        boolean status;
        switch (download.getStatus()) {
            case COMPLETED:
            case NONE:
            case FAILED:
                status = false;
                break;
            default:
                status = true;
                break;
        }
        return status;
    }

    @Override
    public List retry(List<Integer> ids) {
        List<DownloadInfo> firstid = fetchDatabaseManagerWrapper.get(ids);
        List<DownloadInfo> downloadInfoList = new ArrayList<>();
        if (firstid != null) {
            for (int i = 0; i < firstid.size(); i++) {
                DownloadInfo download = firstid.get(i);
                if (download != null) {
                    downloadInfoList.add(download);
                }
            }
        }
        ArrayList<DownloadInfo> retryDownloads = new ArrayList<DownloadInfo>();
        for (DownloadInfo downloadInfo : downloadInfoList) {
            downloadInfo.setStatus(Status.QUEUED);
            downloadInfo.setError(Defaults.getDefaultNoError());//error = defaultNoError;
            retryDownloads.add(downloadInfo);
        }
        fetchDatabaseManagerWrapper.update(retryDownloads);
        startPriorityQueueIfNotStarted();
        return retryDownloads;

    }


    @Override
    public Pair<Download, Boolean> updateRequest(int requestId, Request newRequest) {
        DownloadInfo oldDownloadInfo = fetchDatabaseManagerWrapper.get(requestId);
        if (oldDownloadInfo != null) {
            cancelDownloadsIfDownloading(Collections.<DownloadInfo>singletonList(oldDownloadInfo));

            oldDownloadInfo = fetchDatabaseManagerWrapper.get(requestId);
        }


        if (oldDownloadInfo != null) {
            if (newRequest.getFile() == oldDownloadInfo.getFile()) {
                DownloadInfo newDownloadInfo = TypeConverterExtensions.toDownloadInfo(newRequest,fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());
                newDownloadInfo.setNamespace(namespace);//namespace = namespace;
                newDownloadInfo.setDownloaded(oldDownloadInfo.getDownloaded());//downloaded = oldDownloadInfo.downloaded;
                newDownloadInfo.setTotal(oldDownloadInfo.getTotal());//total = oldDownloadInfo.total;
                if (oldDownloadInfo.getStatus() == Status.DOWNLOADING) {
                    newDownloadInfo.setStatus(Status.QUEUED);//status = Status.QUEUED;
                    newDownloadInfo.setError(Defaults.getDefaultNoError());//error = defaultNoError;
                } else {
                    newDownloadInfo.setStatus(oldDownloadInfo.getStatus());//status = oldDownloadInfo.status;
                    newDownloadInfo.setError(oldDownloadInfo.getError());//error = oldDownloadInfo.error;
                }
                fetchDatabaseManagerWrapper.delete(oldDownloadInfo);
                fetchDatabaseManagerWrapper.insert(newDownloadInfo);
                startPriorityQueueIfNotStarted();

                return new Pair(newDownloadInfo, true);
            } else {

                delete(Collections.singletonList(requestId));
                Pair<Download, Error> enqueuePair = enqueue(newRequest);
                return new Pair(enqueuePair.f, enqueuePair.s == Error.NONE);
            }
        } else {
            throw new FetchException(FetchErrorStrings.REQUEST_DOES_NOT_EXIST);
        }
    }

    @Override
    public List getDownloads() {
        return fetchDatabaseManagerWrapper.get();
    }

    @Override
    public Download getDownload(int id) {
        return fetchDatabaseManagerWrapper.get(id);
    }

    @Override
    public List getDownloads(List<Integer> idList) {

        return ToEmpty(idList);
    }

    private List<DownloadInfo> ToEmpty(List<Integer> idList) {

        List<DownloadInfo> firstid = fetchDatabaseManagerWrapper.get(idList);
        List<DownloadInfo> downloadInfoList = new ArrayList<>();
        if (firstid != null) {
            for (int i = 0; i < firstid.size(); i++) {
                DownloadInfo download = firstid.get(i);
                if (download != null) {
                    downloadInfoList.add(download);
                }
            }
        }
        return downloadInfoList;
    }

    @Override
    public List getDownloadsInGroup(int id) {
        return fetchDatabaseManagerWrapper.getByGroup(id);
    }

    @Override
    public List getDownloadsWithStatus(Status status) {
        return fetchDatabaseManagerWrapper.getByStatus(status);
    }

    @Override
    public List<Integer> getAllGroupIds() {
        return fetchDatabaseManagerWrapper.getAllGroupIds();
    }

    @Override
    public List getDownloadsByTag(String tag) {
        return fetchDatabaseManagerWrapper.getDownloadsByTag(tag);
    }

    @Override
    public List getDownloadsWithStatus(List<Status> statuses) {
        return fetchDatabaseManagerWrapper.getByStatus(statuses);
    }

    @Override
    public List getDownloadsInGroupWithStatus(int groupId, List<Status> statuses) {
        return fetchDatabaseManagerWrapper.getDownloadsInGroupWithStatus(groupId, statuses);
    }

    @Override
    public List getDownloadsByRequestIdentifier(Long identifier) {
        return fetchDatabaseManagerWrapper.getDownloadsByRequestIdentifier(identifier);
    }

    @Override
    public void setGlobalNetworkType(NetworkType networkType) {
        priorityListProcessor.stop();
        priorityListProcessor.setGlobalNetworkType(networkType);//globalNetworkType = networkType;
        List<Integer> ids = downloadManager.getActiveDownloadsIds();
        if (!ids.isEmpty()) {
            List<DownloadInfo> downloads = ToEmpty(ids);
            if (!downloads.isEmpty()) {
                cancelDownloadsIfDownloading(downloads);
                List<DownloadInfo> download = ToEmpty(ids);

                for (DownloadInfo lastdown : download) {
                    if (lastdown.getStatus() == Status.DOWNLOADING) {
                        lastdown.setStatus(Status.QUEUED);//status = Status.QUEUED;
                        lastdown.setError(Defaults.getDefaultNoError());//error = defaultNoError;
                    }
                }
                fetchDatabaseManagerWrapper.update(downloads);
            }
        }
        priorityListProcessor.start();

    }

    @Override
    public void enableLogging(boolean enabled) {
        logger.setEnabled(enabled);
    }

    @Override
    public void addListener(FetchListener listener, boolean notify, boolean autoStart) {
        synchronized (listenerSet) {
            listenerSet.add(listener);
        }
        listenerCoordinator.addListener(listenerId, listener);
        if (notify) {
            List<DownloadInfo> downloads = fetchDatabaseManagerWrapper.get();
            for (DownloadInfo download : downloads) {
                uiHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        switch (download.getStatus()) {
                            case COMPLETED:
                                listener.onCompleted(download);
                                break;
                            case FAILED:
                                listener.onError(download, download.getError(), null);
                                break;
                            case CANCELLED:
                                listener.onCancelled(download);
                                break;
                            case DELETED:
                                listener.onDeleted(download);
                                break;
                            case PAUSED:
                                listener.onPaused(download);
                                break;
                            case QUEUED:
                                listener.onQueued(download, false);
                                break;
                            case REMOVED:
                                listener.onRemoved(download);
                                break;
                            case DOWNLOADING:
                                break;
                            case ADDED:
                                listener.onAdded(download);
                                break;
                            case NONE:
                                break;

                        }
                    }
                });
            }
        }
        if (autoStart) {
            startPriorityQueueIfNotStarted();
        }
    }

    @Override
    public void removeListener(FetchListener listener) {
        synchronized (listenerSet) {
            Iterator<FetchListener> iterator = listenerSet.iterator();
            while (iterator.hasNext()) {
                FetchListener fetchListener = iterator.next();
                if (fetchListener == listener) {
                    iterator.remove();
                    break;
                }
            }
             listenerCoordinator.removeListener(listenerId, listener);
        }
    }

    @Override
    public List getDownloadBlocks(int id) {
        DownloadInfo download = fetchDatabaseManagerWrapper.get(id);
        if (download != null) {
            String fileTempDir = downloadManager.getDownloadFileTempDir(download);
            FileSliceInfo fileSliceInfo = FetchUtils.getFileSliceInfo(FetchUtils.getPreviousSliceCount(download.getId(), fileTempDir), download.getTotal());
            if (download.getTotal() < 1) {
                return Collections.emptyList(); //返回空的 list
            } else if (fileSliceInfo.getSlicingCount() < 2) {
                DownloadBlockInfo downloadBlockInfo = new DownloadBlockInfo();
                downloadBlockInfo.setDownloadId(download.getId());
                downloadBlockInfo.setBlockPosition(1);
                downloadBlockInfo.setStartByte(0);
                downloadBlockInfo.setEndByte(download.getTotal());
                downloadBlockInfo.setDownloadedBytes(download.getDownloaded());
                return Collections.singletonList(downloadBlockInfo);
            } else {
                long counterBytes = 0L;
                List<DownloadBlockInfo> downloadBlocksList = new ArrayList();
                for (int i = 1; i <= fileSliceInfo.getSlicingCount(); i++) {
                    Long startBytes = counterBytes;
                    Long endBytes = null;
                    if (fileSliceInfo.getSlicingCount() == i) {
                        endBytes = download.getTotal();
                    } else {
                        endBytes = counterBytes + fileSliceInfo.getBytesPerFileSlice();
                    }
                    counterBytes = endBytes;
                    DownloadBlockInfo downloadBlockInfo = new DownloadBlockInfo();
                    downloadBlockInfo.setDownloadId(download.getId());
                    downloadBlockInfo.setBlockPosition(i);
                    downloadBlockInfo.setStartByte(startBytes);
                    downloadBlockInfo.setEndByte(endBytes);
                    downloadBlockInfo.setDownloadedBytes(FetchUtils.getSavedDownloadedInfo(download.getId(), i, fileTempDir));
                    downloadBlocksList.add(downloadBlockInfo);
                }
                return downloadBlocksList;
            }
        } else {
            return Collections.emptyList(); //返回空的list
        }


    }

    @Override
    public Long getContentLengthForRequest(Request request, boolean fromServer) {
        DownloadInfo download = fetchDatabaseManagerWrapper.get(request.getId());
        if (download != null && download.getTotal() > 0) {
            return download.getTotal();
        }

        if (fromServer) {
            if (FetchCoreUtils.isFetchFileServerUrl(request.getUrl())) {
                return fileServerDownloader.getRequestContentLength(FetchUtils.getServerRequestFromRequest(request));
            } else {
                return httpDownloader.getRequestContentLength(FetchUtils.getServerRequestFromRequest(request));
            }
        } else {
            return -1L;
        }

    }

    @Override
    public Downloader.Response getServerResponse(String url, Map<String, String> headers) throws IOException {
        Request request = new Request(url, "");
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            request.addHeader(entry.getKey(), entry.getValue());
        }
        Downloader.ServerRequest serverRequest = FetchUtils.getServerRequestFromRequest(request);
        InterruptMonitor interruptMonitor = new InterruptMonitor() {
            @Override
            public boolean isInterrupted() {
                return false;
            }
        };
        if (FetchCoreUtils.isFetchFileServerUrl(request.getUrl())) {
            Downloader.Response response = fileServerDownloader.execute(serverRequest, interruptMonitor);
            if (response != null) {
                Downloader.Response copy = FetchCoreUtils.copyDownloadResponseNoStream(response);
                fileServerDownloader.disconnect(response);
                return copy;
            }
        } else {
            Downloader.Response response = httpDownloader.execute(serverRequest, interruptMonitor);
            if (response != null) {
                Downloader.Response copy = FetchCoreUtils.copyDownloadResponseNoStream(response);
                httpDownloader.disconnect(response);
                return copy;
            }
        }
        throw new IOException(RESPONSE_NOT_SUCCESSFUL);
    }

    @Override
    public List<FileResource> getFetchFileServerCatalog(Request request) {
        //   return fileServerDownloader.getFetchFileServerCatalog(getCatalogServerRequestFromRequest(request))
        List<FileResource> fs=null;
        try {
             fs= fileServerDownloader.getFetchFileServerCatalog(FetchUtils.getCatalogServerRequestFromRequest(request));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fs;
    }

    @Override
    public void setDownloadConcurrentLimit(int downloadConcurrentLimit) {
        priorityListProcessor.stop();
        List<Integer> ids = downloadManager.getActiveDownloadsIds();
        if (!ids.isEmpty()) {
            List<DownloadInfo> downloads = ToEmpty(ids);
            if (!downloads.isEmpty()) {
                cancelDownloadsIfDownloading(downloads);
                downloads = ToEmpty(ids);
                downloadManager.setConcurrentLimit(downloadConcurrentLimit);
                priorityListProcessor.setDownloadConcurrentLimit(downloadConcurrentLimit);
                for (DownloadInfo download : downloads) {
                    if (download.getStatus() == Status.DOWNLOADING) {
                        download.setStatus(Status.QUEUED);
                        download.setError(Defaults.getDefaultNoError());
                    }
                }
                fetchDatabaseManagerWrapper.update(downloads);
            }
        }
        priorityListProcessor.start();

    }

    @Override
    public Download replaceExtras(int id, Extras extras) {

        DownloadInfo downloadInfo = fetchDatabaseManagerWrapper.get(id);
        if (downloadInfo != null) {

            cancelDownloadsIfDownloading(Collections.<DownloadInfo>singletonList(downloadInfo));
            return downloadInfo = fetchDatabaseManagerWrapper.get(id);
        }
        if (downloadInfo != null) {
            DownloadInfo download = fetchDatabaseManagerWrapper.updateExtras(id, extras);
            if (download == null) {
                throw new FetchException(FetchErrorStrings.REQUEST_DOES_NOT_EXIST);
            }

        } else {
            throw new FetchException(FetchErrorStrings.REQUEST_DOES_NOT_EXIST);
        }
        return null;
    }

    @Override
    public boolean hasActiveDownloads(boolean includeAddedDownloads) {
        /* Looper 鸿蒙中的替代暂未找到*/

        if (Thread.currentThread().getId() == EventRunner
            .getMainEventRunner().getThreadId()) {

            new FetchException(FetchErrorStrings.BLOCKING_CALL_ON_UI_THREAD);
        }
        boolean isbool = false;
        if (fetchDatabaseManagerWrapper.getPendingCount(includeAddedDownloads) > 0) {
            isbool = true;
        }
        return isbool;
    }

    @Override
    public Set<FetchListener> getListenerSet() {
       synchronized(listenerSet) {
           return Collections.unmodifiableSet(listenerSet);
       }
       /* kt做性能优化
       synchronized (listenerSet) {
            listenerSet.toSet();
        }*/
    }


    @Override
    public Long getPendingCount() {
        return fetchDatabaseManagerWrapper.getPendingCount(false);
    }

    @Override
    public Download renameCompletedDownloadFile(int id, String newFileName) throws FetchException {
        DownloadInfo download = fetchDatabaseManagerWrapper.get(id);
        if (download == null) {
            throw new FetchException(FetchErrorStrings.REQUEST_DOES_NOT_EXIST);
        }
        if (download.getStatus() != Status.COMPLETED) {
            new FetchException(FetchErrorStrings.FAILED_RENAME_FILE_ASSOCIATED_WITH_INCOMPLETE_DOWNLOAD);
        }
        DownloadInfo downloadWithFile = fetchDatabaseManagerWrapper.getByFile(newFileName);
        if (downloadWithFile != null) {
            throw new FetchException(REQUEST_WITH_FILE_PATH_ALREADY_EXIST);
        }
        DownloadInfo copy = (DownloadInfo) download.copy();
        copy.setId(FetchCoreUtils.getUniqueId(download.getUrl(), newFileName));
        copy.setFile(newFileName);
        Pair<DownloadInfo, Boolean> pair = fetchDatabaseManagerWrapper.insert(copy);
        if (!pair.s) {
            throw new FetchException(FetchErrorStrings.FILE_CANNOT_BE_RENAMED);
        }
        Boolean renamed = null;
        try {
            renamed = storageResolver.renameFile(download.getFile(), newFileName);
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
        if (!renamed) {
            fetchDatabaseManagerWrapper.delete(copy);
            throw new FetchException(FetchErrorStrings.FILE_CANNOT_BE_RENAMED);
        } else {
            fetchDatabaseManagerWrapper.delete(download);
            return pair.f;
        }

    }

    @Override
    public FetchGroup getFetchGroup(int id) {
        return groupInfoProvider.getGroupInfo(id, Reason.OBSERVER_ATTACHED);
    }

    @Override
    public void addFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
        listenerCoordinator.addFetchObserversForDownload(downloadId, fetchObservers);
    }

    @Override
    public void removeFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
          listenerCoordinator.removeFetchObserversForDownload(downloadId, fetchObservers);
    }

    @Override
    public Download resetAutoRetryAttempts(int downloadId, boolean retryDownload) {
        DownloadInfo download = fetchDatabaseManagerWrapper.get(downloadId);

        if (download != null) {
            cancelDownloadsIfDownloading(Collections.singletonList(download));
            if (retryDownload && FetchUtils.canRetryDownload(download)) {
                download.setStatus(Status.QUEUED);
                download.setError(Defaults.getDefaultNoError());
            }
            download.setAutoRetryAttempts(0);
            fetchDatabaseManagerWrapper.update(download);
            startPriorityQueueIfNotStarted();
        }
        return download;

    }

    @Override
    public void close() throws IOException {

        if (isTerminating) {
            return;
        }
        isTerminating = true;
        synchronized (listenerSet) {
            Iterator<FetchListener> iterator = listenerSet.iterator();
            while (iterator.hasNext()) {
                FetchListener listener = iterator.next();
                  listenerCoordinator.removeListener(listenerId, listener);
            }
            listenerSet.clear();
        }
        if (fetchNotificationManager != null) {
               listenerCoordinator.removeNotificationManager(fetchNotificationManager);
              listenerCoordinator.cancelOnGoingNotifications(fetchNotificationManager);
        }
        priorityListProcessor.stop();
        priorityListProcessor.close();
        downloadManager.close();
        FetchModulesBuilder.removeNamespaceInstanceReference(namespace);
    }

    private List<Pair<Download, Error>> enqueueRequests(List<Request> requests) {
        ArrayList<Pair<Download, Error>> results = new ArrayList<>();
        for (Request request : requests) {
            DownloadInfo downloadInfo = TypeConverterExtensions.toDownloadInfo(request, fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());
            downloadInfo.setNamespace(namespace);
            try {
                boolean existing = prepareDownloadInfoForEnqueue(downloadInfo);
                if (downloadInfo.getStatus() != Status.COMPLETED) {
                    if (request.getDownloadOnEnqueue()) {
                        downloadInfo.setStatus(Status.QUEUED);
                    } else {
                        downloadInfo.setStatus(Status.ADDED);

                    }
                    if (!existing) {
                        Pair<DownloadInfo, Boolean> downloadPair = fetchDatabaseManagerWrapper.insert(downloadInfo);
                        results.add(new Pair<>(downloadPair.f, Error.NONE));
                        startPriorityQueueIfNotStarted();
                    } else {
                        fetchDatabaseManagerWrapper.update(downloadInfo);
                        results.add(new Pair<>(downloadInfo, Error.NONE));
                    }
                } else {
                    results.add(new Pair<>(downloadInfo, Error.NONE));
                }
                if (prioritySort == PrioritySort.DESC && !downloadManager.canAccommodateNewDownload()) {
                    priorityListProcessor.pause();
                }
            } catch (Exception e) {
                Error error = FetchErrorUtils.getErrorFromThrowable(e);
                error.setThrowable(e);
                results.add(new Pair<>(downloadInfo, error));
                e.printStackTrace();
            }
        }
        startPriorityQueueIfNotStarted();
        return results;
    }


    private Boolean prepareDownloadInfoForEnqueue( DownloadInfo downloadInfo) throws IOException, DataAbilityRemoteException {
        cancelDownloadsIfDownloading(Collections.singletonList(downloadInfo));
        DownloadInfo existingDownload = fetchDatabaseManagerWrapper.getByFile(downloadInfo.getFile());
        if (existingDownload == null) {
            if (downloadInfo.getEnqueueAction() != EnqueueAction.INCREMENT_FILE_NAME) {
                if (createFileOnEnqueue) {
                     storageResolver.createFile(downloadInfo.getFile(),false);
                }
            }
        } else {
            cancelDownloadsIfDownloading(Collections.singletonList(existingDownload));
            existingDownload = fetchDatabaseManagerWrapper.getByFile(downloadInfo.getFile());
            if (existingDownload != null && existingDownload.getStatus() == Status.DOWNLOADING) {
                existingDownload.setStatus(Status.QUEUED);
                try {
                    fetchDatabaseManagerWrapper.update(existingDownload);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (existingDownload.getStatus() == Status.COMPLETED
                && downloadInfo.getEnqueueAction() == EnqueueAction.UPDATE_ACCORDINGLY) {
                if (!storageResolver.fileExists(existingDownload.getFile())) {
                    try {
                        fetchDatabaseManagerWrapper.delete(existingDownload);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    existingDownload = null;
                    if (downloadInfo.getEnqueueAction() != EnqueueAction.INCREMENT_FILE_NAME) {
                        if (createFileOnEnqueue) {
                             storageResolver.createFile(downloadInfo.getFile(),false);
                        }
                    }
                }
            }
        }

        switch (downloadInfo.getEnqueueAction()) {
            case UPDATE_ACCORDINGLY:
                if (existingDownload != null) {
                    downloadInfo.setDownloaded(existingDownload.getDownloaded());
                    downloadInfo.setTotal(existingDownload.getTotal());
                    downloadInfo.setError(existingDownload.getError());
                    downloadInfo.setStatus(existingDownload.getStatus());
                    if (downloadInfo.getStatus() != Status.COMPLETED) {
                        downloadInfo.setStatus(Status.QUEUED);
                        downloadInfo.setError(Defaults.getDefaultNoError());
                    }
                    if (downloadInfo.getStatus() == Status.COMPLETED && !storageResolver.fileExists(downloadInfo.getFile())) {
                        if (createFileOnEnqueue) {
                             storageResolver.createFile(downloadInfo.getFile(),false);
                        }
                        downloadInfo.setDownloaded(0L);
                        downloadInfo.setTotal(-1L);
                        downloadInfo.setStatus(Status.QUEUED);
                        downloadInfo.setError(Defaults.getDefaultNoError());
                    }
                    return true;
                } else {
                    return false;
                }
            case DO_NOT_ENQUEUE_IF_EXISTING:
                if (existingDownload != null) {
                    throw new FetchException(REQUEST_WITH_FILE_PATH_ALREADY_EXIST);
                } else {
                    return false;
                }
            case REPLACE_EXISTING:
                if (existingDownload != null) {
                    deleteDownloads(Collections.singletonList(existingDownload));
                }
                deleteDownloads(Collections.singletonList(downloadInfo));
                return false;
            case INCREMENT_FILE_NAME:
                if (createFileOnEnqueue) {
                     storageResolver.createFile(downloadInfo.getFile(), true);
                }
                downloadInfo.setFile(downloadInfo.getFile());
                downloadInfo.setId(FetchCoreUtils.getUniqueId(downloadInfo.getUrl(), downloadInfo.getFile()));
                return false;
        }

    return false;
    }
    private void startPriorityQueueIfNotStarted() {
        priorityListProcessor.sendBackOffResetSignal();
        if (priorityListProcessor.isStopped() && !isTerminating) {
            priorityListProcessor.start();
        }
        if (priorityListProcessor.isPaused() && !isTerminating) {
            priorityListProcessor.resume();
        }
    }
}
